﻿using System;
using System.Collections.Generic;
using GeoAPI.Geometries;
using NetTopologySuite.Geometries;
using NetTopologySuite.GeometriesGraph;
using NetTopologySuite.Operation.Overlay;
using NetTopologySuite.Utilities;

namespace NetTopologySuite.Operation.Valid
{
	public class ConnectedInteriorTester
	{
		public static Coordinate FindDifferentPoint(Coordinate[] coord, Coordinate pt)
		{
			foreach (Coordinate coordinate in coord)
			{
				if (!coordinate.Equals(pt))
				{
					return coordinate;
				}
			}
			return null;
		}

		public ConnectedInteriorTester(GeometryGraph geomGraph)
		{
			this._geomGraph = geomGraph;
		}

		public Coordinate Coordinate
		{
			get
			{
				return this._disconnectedRingcoord;
			}
		}

		public bool IsInteriorsConnected()
		{
			IList<Edge> list = new List<Edge>();
			this._geomGraph.ComputeSplitEdges(list);
			PlanarGraph planarGraph = new PlanarGraph(new OverlayNodeFactory());
			planarGraph.AddEdges(list);
			ConnectedInteriorTester.SetInteriorEdgesInResult(planarGraph);
			planarGraph.LinkResultDirectedEdges();
			IList<EdgeRing> edgeRings = this.BuildEdgeRings(planarGraph.EdgeEnds);
			this.VisitShellInteriors(this._geomGraph.Geometry, planarGraph);
			return !this.HasUnvisitedShellEdge(edgeRings);
		}

		private static void SetInteriorEdgesInResult(PlanarGraph graph)
		{
			foreach (EdgeEnd edgeEnd in graph.EdgeEnds)
			{
				DirectedEdge directedEdge = (DirectedEdge)edgeEnd;
				if (directedEdge.Label.GetLocation(0, Positions.Right) == Location.Interior)
				{
					directedEdge.InResult = true;
				}
			}
		}

		private IList<EdgeRing> BuildEdgeRings(IEnumerable<EdgeEnd> dirEdges)
		{
			IList<EdgeRing> list = new List<EdgeRing>();
			foreach (EdgeEnd edgeEnd in dirEdges)
			{
				DirectedEdge directedEdge = (DirectedEdge)edgeEnd;
				if (directedEdge.IsInResult && directedEdge.EdgeRing == null)
				{
					MaximalEdgeRing maximalEdgeRing = new MaximalEdgeRing(directedEdge, this._geometryFactory);
					maximalEdgeRing.LinkDirectedEdgesForMinimalEdgeRings();
					foreach (EdgeRing item in maximalEdgeRing.BuildMinimalRings())
					{
						list.Add(item);
					}
				}
			}
			return list;
		}

		private void VisitShellInteriors(IGeometry g, PlanarGraph graph)
		{
			if (g is IPolygon)
			{
				IPolygon polygon = (IPolygon)g;
				this.VisitInteriorRing(polygon.Shell, graph);
			}
			if (g is IMultiPolygon)
			{
				foreach (IPolygon polygon2 in ((IMultiPolygon)g).Geometries)
				{
					this.VisitInteriorRing(polygon2.Shell, graph);
				}
			}
		}

		private void VisitInteriorRing(ILineString ring, PlanarGraph graph)
		{
			Coordinate[] coordinates = ring.Coordinates;
			Coordinate coordinate = coordinates[0];
			Coordinate p = ConnectedInteriorTester.FindDifferentPoint(coordinates, coordinate);
			Edge e = graph.FindEdgeInSameDirection(coordinate, p);
			DirectedEdge directedEdge = (DirectedEdge)graph.FindEdgeEnd(e);
			DirectedEdge directedEdge2 = null;
			if (directedEdge.Label.GetLocation(0, Positions.Right) == Location.Interior)
			{
				directedEdge2 = directedEdge;
			}
			else if (directedEdge.Sym.Label.GetLocation(0, Positions.Right) == Location.Interior)
			{
				directedEdge2 = directedEdge.Sym;
			}
			Assert.IsTrue(directedEdge2 != null, "unable to find dirEdge with Interior on RHS");
			this.VisitLinkedDirectedEdges(directedEdge2);
		}

		protected void VisitLinkedDirectedEdges(DirectedEdge start)
		{
			DirectedEdge directedEdge = start;
			do
			{
				Assert.IsTrue(directedEdge != null, "found null Directed Edge");
				directedEdge.Visited = true;
				directedEdge = directedEdge.Next;
			}
			while (directedEdge != start);
		}

		private bool HasUnvisitedShellEdge(IList<EdgeRing> edgeRings)
		{
			for (int i = 0; i < edgeRings.Count; i++)
			{
				EdgeRing edgeRing = edgeRings[i];
				if (!edgeRing.IsHole)
				{
					IList<DirectedEdge> edges = edgeRing.Edges;
					DirectedEdge directedEdge = edges[0];
					if (directedEdge.Label.GetLocation(0, Positions.Right) == Location.Interior)
					{
						for (int j = 0; j < edges.Count; j++)
						{
							directedEdge = edges[j];
							if (!directedEdge.IsVisited)
							{
								this._disconnectedRingcoord = directedEdge.Coordinate;
								return true;
							}
						}
					}
				}
			}
			return false;
		}

		private readonly GeometryFactory _geometryFactory = new GeometryFactory();

		private readonly GeometryGraph _geomGraph;

		private Coordinate _disconnectedRingcoord;
	}
}
